﻿using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using Internal.KuiHuaBaoDian.Services.UI.Alert;
using KuiHuaBaoDian.Services.UGUI.Alert;

namespace KuiHuaBaoDian.Services.UGUI.Alert {

    public static class AlertService {

        [Flags]
        public enum ButtonFlag {
            None = 0,
            Done = 1,
            OK = 2,
            Cancel = 4,
            Close = 8,

            OKAndCancel = OK | Cancel,
        }

        /// <summary>
        /// 单例
        /// </summary>
        public static IAlertService Instance => m_Instance ??= KHBD.TypeMap.Instantiate<IAlertService>();
        private static IAlertService m_Instance;

        public static void Show<TUI>(IAlertOptions options, Func<ButtonFlag, UniTask> callback, IUIOptions uiOptions = null, IUIOpenOptions uIOpenOptions = null) where TUI : IUI => Instance.Show<TUI>(options, callback, uiOptions, uIOpenOptions);

        public static UniTask<ButtonFlag> Show<TUI>(IAlertOptions options, IUIOptions uiOptions = null, IUIOpenOptions uIOpenOptions = null) where TUI : IUI => Instance.Show<TUI>(options, uiOptions, uIOpenOptions);
    }

    public interface IAlertService {

        void Show<TUI>(IAlertOptions options, Func<AlertService.ButtonFlag, UniTask> callback, IUIOptions uiOptions = null, IUIOpenOptions uIOpenOptions = null) where TUI : IUI;

        UniTask<AlertService.ButtonFlag> Show<TUI>(IAlertOptions options, IUIOptions uiOptions = null, IUIOpenOptions uIOpenOptions = null) where TUI : IUI;
    }

    public interface IAlertOptions {

        IText Content { get; }

        IText Title { get; }

        AlertService.ButtonFlag Flags { get; }
    }

    [Implement(typeof(IAlertService))]
    internal sealed class KHBD_AlertService : IAlertService, IAlertServiceInternal, IService {

        private record Record(IAlertOptions Options, IUIOptions UIOptions, IUIOpenOptions UIOpenOptions, Func<AlertService.ButtonFlag, UniTask> Callback);

        private readonly Queue<Record> m_AlertRecordQueue = new();

        private Record m_ShowingAlertRecord = null;

        IAlertOptions IAlertServiceInternal.ShowingOptions => m_ShowingAlertRecord?.Options;

        void IService.OnApplicationQuit() {
            m_AlertRecordQueue.Clear();
            m_ShowingAlertRecord = null;
        }

        public void Show<TUI>(IAlertOptions options, Func<AlertService.ButtonFlag, UniTask> callback, IUIOptions uiOptions = null, IUIOpenOptions uIOpenOptions = null) where TUI : IUI {
            m_AlertRecordQueue.Enqueue(new Record(options, uiOptions, uIOpenOptions, callback));
            Show<TUI>().Forget();
        }

        public async UniTask<AlertService.ButtonFlag> Show<TUI>(IAlertOptions options, IUIOptions uiOptions = null, IUIOpenOptions uIOpenOptions = null) where TUI : IUI {
            var result = AlertService.ButtonFlag.None;
            m_AlertRecordQueue.Enqueue(new Record(options, uiOptions, uIOpenOptions, flag => {
                result = flag;
                return UniTask.CompletedTask;
            }));
            Show<TUI>().Forget();
            await KHBD.Context.WaitUntil(() => result != AlertService.ButtonFlag.None, CancellationToken.None);
            return result;
        }

        private async UniTaskVoid Show<TUI>() where TUI : IUI {
            if (m_ShowingAlertRecord == null && m_AlertRecordQueue.Count > 0) {
                m_ShowingAlertRecord = m_AlertRecordQueue.Dequeue();
                var uiHandle = await UIService.Instance.Open<TUI, AlertUIHandle>(m_ShowingAlertRecord.UIOptions, m_ShowingAlertRecord.UIOpenOptions == null ? UIOpenOptions.Instantiate(true, -0.5f, true) : m_ShowingAlertRecord.UIOpenOptions, null);
                var flag = await uiHandle.WaitClickButton();
                UIService.Instance.Close(uiHandle);
                var task = m_ShowingAlertRecord.Callback?.Invoke(flag);
                if (task.HasValue) {
                    await task.Value;
                }
                if (m_ShowingAlertRecord.Options is IDisposable disposable) {
                    disposable.Dispose();
                }
                m_ShowingAlertRecord = null;
                Show<TUI>().Forget();
            } else {
                await UniTask.Yield();
            }
        }
    }
}

namespace Internal.KuiHuaBaoDian.Services.UI.Alert {

    public interface IAlertServiceInternal {

        IAlertOptions ShowingOptions { get; }
    }
}
